//
//  ECDialogLineRecorder.m
//  ECCoreLibrary
//
//  Created by Matt Austin on 4/28/14.
//  Copyright (c) 2014 EnglishCentral, Inc. All rights reserved.
//

#import "MujiDubRecorder.h"

//#import "ECCommon.h"
//#import "ECSpeexEncoder.h"
//#import "ECDialog.h"
//#import "ECActivity.h"
//#import "ECAudioFileCleanUpOperation.h"
//#import "ECCoreLibraryConstants.h"
//#import "ECCoreLibraryResourcesBundleUtility.h"
//#import "ECDialogLine.h"
#import "MujiDubFilePathUtility.h"
//#import "ECCoreLibraryDefines.h"
//#import "DialogLinesModel.h"

typedef NS_ENUM(NSUInteger, ECDialogLineRecorderFileExtensionKey)
{
   ECDialogLineRecorderFileExtensionKeyWav
};

@interface MujiDubRecorder ()

@property (nonatomic, readwrite, copy)    NSString *wavFilePath;
@property (nonatomic, readwrite, strong)  AVAudioRecorder *audioRecorder;
@property (nonatomic, readwrite)          MujiDubDialogLineRecordingState recordingState;
@property (nonatomic, readwrite, strong)  NSError *recordingError;
@property (nonatomic, copy)               NSString *directoryPath;
@property (nonatomic, assign)             NSTimeInterval currentAudioTime;

// File Path Helpers
- (BOOL)createNewAudioRecorderWithFilePath:(NSString *)filePath;
- (void)deleteRecordingFile;

// Misc Helpers
- (void)configureAudioRecorderMetering;

// Recording State Value Change Handlers.
- (void)handleRecordingStateValueChange;
- (void)handleRecordingStateValueChangedToCancelled;
- (void)handleRecordingStateValueChangedToRecording;
- (void)handleRecordingStateValueChangedToFailed;
- (void)handleRecordingStateValueChangedToPaused;

@property (nonatomic, assign) BOOL   isCancel;


@end

@implementation MujiDubRecorder


- (NSDictionary *)recordingSettings
{
   if (! _recordingSettings)
   {
      _recordingSettings = @{AVFormatIDKey             : @(kAudioFormatLinearPCM),
                             AVSampleRateKey           : @(44100.0),
                             AVNumberOfChannelsKey     : @(2),
                             AVLinearPCMBitDepthKey    : @(16),
                             AVLinearPCMIsBigEndianKey : @NO,
                             AVLinearPCMIsFloatKey     : @NO};
   }
   
   return _recordingSettings;
}


- (void)setRecordingState:(MujiDubDialogLineRecordingState)recordingState
{
   static NSString * const kDialogLineRecordingStateKey = @"recordingState";
   if (_recordingState != recordingState)
   {
      [self willChangeValueForKey:kDialogLineRecordingStateKey];
      _recordingState = recordingState;
      [self handleRecordingStateValueChange];
      [self didChangeValueForKey:kDialogLineRecordingStateKey];
   }
}

#pragma mark -
#pragma mark Initializers

- (id)init
{
   self = [super init];
   if (self)
   {
      _maxRecordingDuration = 30.0;
      _currentAudioTime = 0;
   }
   
   return self;
}
- (id)initWithInfo:(NSDictionary *)info path:(NSString *)directoryPath userId:(NSString *)userId;
{
        self.isCancel = NO;
    
        self = [self init];
        if (self)
        {
            NSParameterAssert(directoryPath);
    
            NSString *dialogId = [info objectForKey:@"dialogId"];
            NSString *dialogLineID = [info objectForKey:@"dialogLineID"];

            if (dialogId.length == 0 || dialogLineID.length == 0) {
                return nil;
            }
            
            NSString *path = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@/%@/sectionData", userId, dialogId]];
            NSFileManager *fileManager = [NSFileManager defaultManager];
            NSError *outError = nil;

            if ([fileManager fileExistsAtPath:path])
            {
                //[_mutablePaths setObject:userAudio forKey:@(MujiDubFilePathDirectoryUserAudio)];
            }
            else
            {
                if ([fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&outError])
                {
                    
                   // [_mutablePaths setObject:userAudio forKey:@(MujiDubFilePathDirectoryUserAudio)];
                }
            }
            
            _directoryPath = [path copy];
            
            NSString *fileName = [NSString stringWithFormat:@"%@_%@_%@.wav",
                                  userId,
                                  dialogId,
                                  dialogLineID];
            _wavFilePath = [_directoryPath stringByAppendingPathComponent:fileName];
            

            
        }
        return self;
}
//- (id)initWithDialogLineModel:(DialogLinesModel*) model path:(NSString *)directoryPath userId:(NSString *)userId
//
//{
//    self.isCancel = NO;
//    
//    self = [self init];
//    if (self)
//    {
//        
//        NSParameterAssert(directoryPath);
//        
//        _directoryPath = [directoryPath copy];
//        
//        NSString *fileName = [NSString stringWithFormat:@"%@_%@_%@.wav",
//                              userId,
//                              model.dialogId,
//                              model.dialogLineID];
//        
//        _wavFilePath = [_directoryPath stringByAppendingPathComponent:fileName];
//    }
//    
//    return self;
//
//    
//    
//#if 0
//   self = [self init];
//   if (self)
//   {
//     
//      NSParameterAssert(directoryPath);
//      
//      _directoryPath = [directoryPath copy];
//     
//     NSString *fileName = [NSString stringWithFormat:@"%@_%@.wav",
//                            model.dialogId,
//                            model.dialogLineID];
//      
//       
//      _wavFilePath = [_directoryPath stringByAppendingPathComponent:fileName];
//   }
//   
//   return self;
//    
//#endif
//    
//}

#pragma mark -
#pragma mark Public Methods

- (void)startRecording
{
    AVAudioSession * session = [AVAudioSession sharedInstance];
    NSError * sessionError;
    [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
    if(session == nil)
        NSLog(@"Error creating session: %@", [sessionError description]);
    else
        [session setActive:YES error:nil];
    
   if (!self.audioRecorder)
   {
      if (![self createNewAudioRecorderWithFilePath:self.wavFilePath])
      {
         [self setRecordingState:MujiDubDialogLineRecordingStateFailed];
         [self audioRecorderDidFinishRecording:self.audioRecorder successfully:NO];
         return;
      }
      
     // ECDebugLog(@"Audio Path: %@", self.wavFilePath);
      
      [self configureAudioRecorderMetering];
      
      // Prepare to record.
      [[self audioRecorder] prepareToRecord];
      [[self audioRecorder] recordForDuration:self.maxRecordingDuration];
   }
   
   [self setRecordingState:MujiDubDialogLineRecordingStateRecording];
}

- (void)stopRecording
{
    //&& self.audioRecorder.isRecording
   if (self.audioRecorder && self.recordingState == MujiDubDialogLineRecordingStateRecording)
   {
      self.currentAudioTime = self.audioRecorder.currentTime;
      [[self audioRecorder] stop];
   }
    
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];  //此处需要恢复设置回放标志，否则会导致其它播放声音也会变小
}

- (void)cancelRecording
{
   [self setRecordingState:MujiDubDialogLineRecordingStateCancelled];
    
    
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];  //此处需要恢复设置回放标志，否则会导致其它播放声音也会变小

}

- (void)pauseRecording
{
   [self setRecordingState:MujiDubDialogLineRecordingStatePaused];
}

- (BOOL)didRecordingReachMaxDuration
{
   return ((self.currentAudioTime >= self.maxRecordingDuration) ? YES : NO);
}

#pragma mark -
#pragma mark Private Helpers

- (BOOL)createNewAudioRecorderWithFilePath:(NSString *)filePath
{
   BOOL didCreate = YES;
   
   @try
   {
      NSParameterAssert(filePath);

      NSURL *fileURL = [NSURL fileURLWithPath:filePath];
      NSError *outError;
      self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:fileURL
                                                       settings:self.recordingSettings
                                                          error:&outError];
      self.recordingError = outError;
      
      if (!self.audioRecorder)
      {
         didCreate = NO;
      }
      else
      {
         [[self audioRecorder] setDelegate:self];
      }
   }
   @catch (NSException *exception)
   {
      NSDictionary *userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"Audio Recorder Error", nil),
                                 NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"Cannot create a file with an invalid file name", nil)};
      
      self.recordingError = [NSError errorWithDomain:NSCocoaErrorDomain
                                                code:NSFileReadInvalidFileNameError
                                            userInfo:userInfo];
      didCreate = NO;
   }
   @finally
   {
      return didCreate;
   }
}


- (void)deleteRecordingFile
{
   if (self.wavFilePath)
   {
      NSError *outError = nil;
      if (![[MujiDubFilePathUtility sharedUtility] removeFileWithPath:self.wavFilePath error:&outError])
      {
         self.recordingError = outError;
//         ECDebugLog(@"%@ :: Failed to remove item at path:%@ with error:%@",
//                    NSStringFromClass([self class]),
//                    self.wavFilePath,
//                    [outError description]);
      }
   }
}

#pragma mark Misc Helpers


- (void)configureAudioRecorderMetering
{
   // Ask the data source if the if metering should be enabled or not.  This dataSource
   // delegate method is required, so there is no need to check to see if the
   // object responds.  However we need to ensure that data source has in fact
   // been set:
   BOOL enabledMetering = NO;
   
   if ([[self dataSource] respondsToSelector:@selector(enableMeteringForDialogLineRecorder:)])
   {
      enabledMetering = [[self dataSource] enableMeteringForDialogLineRecorder:self];
   }
   
   [[self audioRecorder] setMeteringEnabled:enabledMetering];
}


#pragma mark Recording State Value Change Handlers

- (void)handleRecordingStateValueChange
{
   switch (self.recordingState)
   {
      case MujiDubDialogLineRecordingStateRecording:
         [self handleRecordingStateValueChangedToRecording];
         break;
      case MujiDubDialogLineRecordingStateCancelled:
         [self handleRecordingStateValueChangedToCancelled];
         break;
      case MujiDubDialogLineRecordingStateFailed:
         [self handleRecordingStateValueChangedToFailed];
         break;
      case MujiDubDialogLineRecordingStatePaused:
         [self handleRecordingStateValueChangedToPaused];
         break;
      default:
         // No need to handle successful state value changed as the delegate
         // method will alert observing objects.
         break;
   }
}

- (void)handleRecordingStateValueChangedToCancelled
{
   if (self.audioRecorder)
   {
      if (self.audioRecorder.isRecording)
      {
         self.currentAudioTime = self.audioRecorder.currentTime;
         [[self audioRecorder] stop];
      }
       self.isCancel = YES;
      [[self audioRecorder] deleteRecording];
      [self setWavFilePath:nil];
   }
}

- (void)handleRecordingStateValueChangedToRecording
{
   if (self.audioRecorder && !self.audioRecorder.isRecording)
   {
      [[self audioRecorder] record];
   }
}

- (void)handleRecordingStateValueChangedToFailed
{
   if (self.audioRecorder)
   {
      if (self.audioRecorder.isRecording)
      {
         self.currentAudioTime = self.audioRecorder.currentTime;
         [[self audioRecorder] stop];
      }
      
      [[self audioRecorder] deleteRecording];
      [self setWavFilePath:nil];
   }
}

- (void)handleRecordingStateValueChangedToPaused
{
   if (self.audioRecorder && self.audioRecorder.isRecording)
   {
      [[self audioRecorder] pause];
   }
}


#pragma mark -
#pragma mark AVAudioRecorderDelegate

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder
                           successfully:(BOOL)flag
{
   if (flag)
   {
      [self setRecordingState:MujiDubDialogLineRecordingStateSuccessful];
   }

    if (self.isCancel) {
        [[self delegate] dialogLineRecorder:self
                                  withState:MujiDubDialogLineRecordingStateCancelled
                                      error:self.recordingError];

    }else {
        [[self delegate] dialogLineRecorder:self
                                  withState:self.recordingState
                                      error:self.recordingError];

    }
    
   
//   if ([[self delegate] respondsToSelector:@selector(dialogLineRecorder:didFinishRecordingForDialogLineModel:withState:error:)])
   {
//      [[self delegate] dialogLineRecorder:self
//     didFinishRecordingForDialogLineModel:self.dialogLineModel
//                                withState:self.recordingState
//                                    error:self.recordingError];
   }
}

- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder
                                   error:(NSError *)error
{
   self.recordingError = error;
   [self setRecordingState:MujiDubDialogLineRecordingStateFailed];
}

- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder
{
   if ([[self delegate] respondsToSelector:@selector(dialogLineRecorderDidBeginInterruption:)])
   {
      [[self delegate] dialogLineRecorderDidBeginInterruption:self];
   }
   else
   {
      NSDictionary *userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"Recording Error", nil),
                                 NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"Session was interrupted", nil)};
      
      self.recordingError = [NSError errorWithDomain:AVFoundationErrorDomain
                                                code:AVErrorSessionWasInterrupted
                                            userInfo:userInfo];
      
      [self setRecordingState:MujiDubDialogLineRecordingStatePaused];
   }
}

// Available in iOS 6.0 and >
//
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder
                         withOptions:(NSUInteger)options
{
   if ([[self delegate] respondsToSelector:@selector(dialogLineRecorderDidEndInterruption:)])
   {
      [[self delegate] dialogLineRecorderDidEndInterruption:self];
   }
   else
   {
      [self setRecordingState:MujiDubDialogLineRecordingStateCancelled];
   }
}




@end

